﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
using BingoServerCommon.Common;

public enum PlayState
{
    PS_WAIT,
    PS_PLAY,
    PS_NONE,
}
class UserSelectObject
{
    public int cardNum;//卡片数
    public int multNum;//倍数
    public bool bRobot;//是否机器人
    public UserSelectObject(int _card_num,int _mult_num,bool _robot)
    {
        cardNum = _card_num;
        multNum = _mult_num;
        bRobot = _robot;
    }
}
class RankUser
{
    public string name;
    public string icon;
    public int rank;
}

namespace BingoGameServerConsole
{
    public partial class Room: BaseRoom
    {
        private int maxUser = 100;
        private int roomWaitTime = 45;//45;//房间等待时间 秒
        private int gameTimeOut = 92;//92;//游戏超时结束时间 秒
        private int checkTime = 60 * 15;//检测房间真人情况 秒 如果这个时间内 没有真人 就将房间闲置
        private int headTenTime = 30;//头十球的时间
        

        public int id;

        private Dictionary<long, long> selectPlayerDict = new Dictionary<long, long>();//选择卡片用户  每回合只能选择一次
        private Dictionary<long, User> realPlayDict = new Dictionary<long, User>();//真人游戏玩家  用户群发
        private List<RankUser> bingoList = new List<RankUser>();//bingo 用户排名先后
        private Dictionary<long, UserSelectObject> backCardDict = new Dictionary<long, UserSelectObject>();//备份记录卡片数
        private Dictionary<long, int> bingoCountDict = new Dictionary<long, int>();//用户bingo记录
        private List<byte> ballList = new List<byte>();//球

        private long sec_lookUserNum = 0;// 旁观用户数量 安全原子操作
        private long sec_playUserNum = 0;// 游戏用户数量 安全原子操作
        private long securityState = 0;//安全状态  安全原子操作  和state 同步

        private PlayState state = PlayState.PS_NONE;
        

        private long startTurnTime = 0;
        private long startGameTime = 0;//游戏开始时间
        private long checkPlayTime = 0;//检测真人时间
        private int cardNum = 0;//卡片数量
        private int bingoNum = 0;//bingo数量
        private int playUserNum = 0;//游戏玩家 本线程使用 和 sec_playUserNum 同步
        private int lookUserNum = 0;//旁观玩家 本线程使用 和 sec_lookUserNum 同步
        private int selectCardRealPlayNum = 0;//选卡真人数量

        private Random rd = new Random();//随机实例 以时间为因子

        public Room(int _id)
        {
            id = _id;
            
            
        }
        public int GetCardNum()
        {
            return cardNum;
        }
        public int GetPlayerUserNum()
        {
            return playUserNum;
        }

        #region 多线程访问  注意安全
        /// <summary>
        /// 是否可以进入
        /// </summary>
        /// <returns></returns>
        public bool IsEnter()
        {
            ///游戏在 游戏状态下 是不允许进入的
            PlayState tmp = (PlayState)Interlocked.Read(ref securityState);
            if (tmp == PlayState.PS_PLAY)
                return false;
            ///房间满了 也不允许进入
            long tmp_wait = Interlocked.Read(ref sec_lookUserNum);
            long tmp_play = Interlocked.Read(ref sec_playUserNum);
            if (tmp_wait + tmp_play >= maxUser)
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// 获得安全状态
        /// </summary>
        /// <returns></returns>
        public PlayState GetSecurityState()
        {
            long tmp = Interlocked.Read(ref securityState);
            return (PlayState)tmp;
        }
        
        #endregion
        
        public void EnterRoom(User player)
        {
            if( state == PlayState.PS_NONE)
            {//房间还未启动
                StartTurn();
            }
            //玩家进入房间  先加入到等待列表
            Interlocked.Increment(ref sec_lookUserNum);// 旁观用户增加
            lookUserNum++;
            if ( !player.bRobot)
            {
                realPlayDict.Add(player.id, player);
            }

            Console.WriteLine($"玩家 {player.id} 进入房间 {id}");

        }
        public void LeaveRoom(User player)
        {
            cardNum -= player.selectCardNum;

            if (state == PlayState.PS_WAIT)
            {
                if(player.selectCardNum>0)
                {// bingo 变化
                    bingoNum = GetBingoNum();
                }

            }

            if(player.selectCardNum<0)
            {
                Interlocked.Decrement(ref sec_lookUserNum);//旁观用户递减
                lookUserNum--;
            }
            else
            {
                Interlocked.Decrement(ref sec_playUserNum);//游戏用户递减
                playUserNum--;


            }
            if (!player.bRobot)
                realPlayDict.Remove(player.id);

            Console.WriteLine($"玩家 {player.id} 离开房间 {id}");

        }
        public void StartTurn()
        {

            if (state == PlayState.PS_WAIT)
                return;
            bingoCountDict.Clear();
            bingoList.Clear();
            selectPlayerDict.Clear();
            cardNum = 0;
            selectCardRealPlayNum = 0;
            //卡片数变化
            foreach (KeyValuePair<long,UserSelectObject> kvp in backCardDict)
            {
                cardNum += kvp.Value.cardNum;
                selectPlayerDict.Add(kvp.Key, kvp.Key);
                if(!kvp.Value.bRobot)
                {
                    selectCardRealPlayNum++;
                }
            }

            //重置真人的选卡记录
            foreach(KeyValuePair<long,User> kvp in realPlayDict)
            {
                kvp.Value.Reset();
            }

            //设置等待状态
            SetState(PlayState.PS_WAIT);

            ///等待45秒
            startTurnTime = DateTime.Now.Ticks;
            bingoNum = GetBingoNum();
            Interlocked.Exchange(ref sec_playUserNum, backCardDict.Count);//游戏人数变化
            playUserNum = backCardDict.Count;


            backCardDict.Clear();

            //广播开始新一轮消息
            Package pack = new Package(BingoClientModel.EnumPackCommand.GC_StartNewTurn);
            BroadcastMessage(pack);

            ///开始机器人
            StartRobot();

            Console.WriteLine($"房间 {id} 新一轮开始");


        }
        public void StartGame()
        {
            
            ///结束等待状态  开始游戏状态
            SetState(PlayState.PS_PLAY);

            startGameTime = DateTime.Now.Ticks;

            //生成30个球
            GenerateBall();

            //广播游戏开始消息
            Package pack = new Package(BingoClientModel.EnumPackCommand.GC_StartGame);
            pack.Write(ballList.Count);
            for(int i=0;i<ballList.Count;i++)
            {
                pack.Write(ballList[i]);
            }
            BroadcastMessage(pack);

            Console.WriteLine($"房间 {id} 游戏开始");
        }
        private void GenerateBall()
        {
            ballList.Clear();

            List<byte> allBall = new List<byte>();
            for (byte i = 1; i <= 75; i++)
                allBall.Add(i);
            
            for (int i=0;i<30;i++)
            {
                
                int index = rd.Next(allBall.Count);
                allBall.RemoveAt(index);

                ballList.Add(allBall[index]);
            }
        }
        private void SetState(PlayState ps )
        {
            state = ps;
            Interlocked.Exchange(ref securityState, (long)state);
            Console.WriteLine($"房间 {id} 状态发生变化 state={ps}");
        }
        public void BroadcastMessage(Package pack)
        {///广播消息出去 
            foreach (KeyValuePair<long,User> kvp in realPlayDict)
            {
                //发送消息出去
                GameTcpServer.SendPackage(kvp.Value.connId, pack);
            }

        }
        /// <summary>
        /// 用户选择卡片
        /// </summary>
        /// <param name="num"></param>
        public void UserSelectCard(User user,int num,int idouble)
        {
            if( selectPlayerDict.ContainsKey(user.id) )
            {
                return;//如果本回合已经选择了 则返回  不处理
            }
            selectPlayerDict.Add(user.id, user.id);

            byte bStartUp=0;
            if (state==PlayState.PS_WAIT)
            {
                cardNum += num;//卡片数量增加
                bingoNum = GetBingoNum();
                Interlocked.Increment(ref sec_playUserNum);//游戏人数+1
                playUserNum++;
                Interlocked.Decrement(ref sec_lookUserNum);//旁观人数-1
                lookUserNum--;
                if(!user.bRobot)
                    selectCardRealPlayNum++;
            }
            else if(state == PlayState.PS_PLAY)
            {
                long curTicks = DateTime.Now.Ticks;
                if(curTicks-startGameTime<=10000000* headTenTime)//如果游戏中 开始未超过30秒 则算卡片数 直接进入游戏
                {
                    cardNum += num;//卡片数量增加
                    bingoNum = GetBingoNum();
                    Interlocked.Increment(ref sec_playUserNum);//游戏人数+1
                    playUserNum++;
                    Interlocked.Decrement(ref sec_lookUserNum);//旁观人数-1
                    lookUserNum--;
                    if (!user.bRobot)
                        selectCardRealPlayNum++;

                    bStartUp = 1;
                }
                else
                {//将卡片数记录下来等待 下个回合使用
                    UserSelectObject obj = new UserSelectObject(num,idouble,user.bRobot);
                    backCardDict.Add(user.id, obj);
                }
            }

            user.selectCardNum = num;
            user.selectMult = idouble;

            Package pack = new Package(BingoClientModel.EnumPackCommand.GC_SelectCard_Ret);
            pack.Write(bStartUp);
            pack.Write(cardNum);
            pack.Write(bingoNum);
            pack.Write(playUserNum);
            
            GameTcpServer.SendPackage(user.connId, pack);

        }
        public void Bingo(User user)
        {
            if (state == PlayState.PS_WAIT)
                return;

            if (!bingoCountDict.ContainsKey(user.id))
                bingoCountDict.Add(user.id, 0);
            int num =bingoCountDict[user.id];
            if (num >= user.selectCardNum)
                return;//如果BINGO次数 大于 选择卡的数量 则视为非法
            bingoCountDict[user.id] = num+ 1;
            Console.WriteLine($"用户 {user.id} BINGO  机器人：{user.bRobot}" );
            bingoNum--;
            RankUser ru = new RankUser();
            ru.name = user.name;
            ru.icon = user.icon;
            ru.rank = bingoList.Count+1;
            bingoList.Add(ru);

            //bingo返回数据
            Package pack = new Package(BingoClientModel.EnumPackCommand.GC_BingoRet);
            pack.Write(ru.rank);
            pack.Write(ru.name);
            pack.Write(ru.icon);
            pack.Write(bingoNum);
            BroadcastMessage(pack);

            if(bingoNum==0)
            {///游戏结束 开始新一轮
                StartTurn();

            }

            
        }
        
        public void  Update(long ticks)
        {
            if (state == PlayState.PS_NONE)
            {///房间未启动状态 不刷新
                return;
            }
            if (state == PlayState.PS_WAIT)
            {
                if (ticks - startTurnTime >= 10000000 * roomWaitTime)
                {//等待时间到了
                    StartGame();
                }
            }
            if (state == PlayState.PS_PLAY)
            {
                long tmp = ticks - startGameTime;
                if (ticks - startGameTime >= 10000000 * gameTimeOut)
                {//游戏超时 或者 bingo数小于=0  开始新一轮
                    StartTurn();
                }

            }
            if(ticks- checkPlayTime >= 10000000* checkTime)
            {
                if( state!= PlayState.PS_NONE && realPlayDict.Count<=0)///如果房间不在闲置状态 并且 没有真人
                    Idle();
                checkPlayTime = ticks;
            }
            ///更新机器人逻辑
            UpdateRobot(ticks);
        }
        /// <summary>
        /// 闲置房间
        /// </summary>
        private void Idle()
        {
            //初始状态
            state = PlayState.PS_NONE;
            Interlocked.Exchange(ref securityState, 2);
            //清空机器人
            ClearRobot();
            Interlocked.Exchange(ref sec_lookUserNum, 0);
            Interlocked.Exchange(ref sec_playUserNum, 0);
            backCardDict.Clear();
            realPlayDict.Clear();
            bingoList.Clear();
            playUserNum = 0;
            lookUserNum = 0;
            cardNum = 0;

            Console.WriteLine($"房间 {id} 由于长期没有真人，系统 将回收闲置");
        }
        private int GetBingoNum()
        {
            return  cardNum / 7;
        }

        public void RequestRoomInfo(IntPtr connId)
        {
            Package pack = new Package(BingoClientModel.EnumPackCommand.GC_RequestRoomInfo_Ret);
            pack.Write(cardNum);
            pack.Write(playUserNum);
            pack.Write(bingoNum);

            pack.Write((short)state);//游戏状态  0 等待  ；1 游戏
            
            if(state== PlayState.PS_WAIT)
            {
                long diff = 45-(DateTime.Now.Ticks - startTurnTime) / 10000000;//倒计时秒
                pack.Write((short)diff);
            }
            else if(state == PlayState.PS_PLAY)
            {//游戏中 将球和 游戏开始后进行的时间 写入包
                pack.Write(ballList.Count);
                for (int i = 0; i < ballList.Count; i++)
                {
                    pack.Write(ballList[i]);
                }
                long time = (DateTime.Now.Ticks - startGameTime)/ 10000000;//秒
                pack.Write((int)time);
            }

            int len = 0;
            if(bingoList.Count<=5)
            {
                len = bingoList.Count;
                pack.Write(len);
                for(int i=0;i<len;i++)
                {
                    RankUser tmp_user = bingoList[i];
                    pack.Write(i + 1);//排名
                    pack.Write(tmp_user.name);
                    pack.Write(tmp_user.icon);
                    
                }
            }
            else
            {
                len = 5;
                pack.Write(len);
                //前三
                for (int i = 0; i < 3; i++)
                {
                    RankUser tmp_user = bingoList[i];
                    pack.Write(i + 1);//排名
                    pack.Write(tmp_user.name);
                    pack.Write(tmp_user.icon);
                    
                }
                //后2
                for (int i = bingoList.Count-2; i < bingoList.Count; i++)
                {
                    RankUser tmp_user = bingoList[i];
                    pack.Write(i + 1);//排名
                    pack.Write(tmp_user.name);
                    pack.Write(tmp_user.icon);
                   
                }
            }

            GameTcpServer.SendPackage(connId,pack);
            
        }
        private void SendStartGame()
        {

        }

    }
}
